#include "CommonFunc.h"
#include "tools/cJSON.h"
#include <fstream>
#include <regex>

#include <QHostAddress>
#include <QNetworkInterface>
#include <QTextCodec>
#include <QImageReader>

QAction* NewAction(const QString strName, const QString strStatusTip, const char *amember)
{
    QAction *pActNew = new QAction(amember);
    pActNew->setText(strName);
    pActNew->setStatusTip(strStatusTip);
    return pActNew;
}

QPushButton *NewPTBWithIcon(const QString strTitle, const QString strTip, const QIcon &icon, QWidget *parent)
{
    QPushButton *pPTB = new QPushButton(parent);
    if(nullptr != pPTB)
    {
        pPTB->setText(strTitle);
        pPTB->setToolTip(strTip);
        pPTB->setStatusTip(strTip);
        pPTB->setFlat(true);
        pPTB->setIcon(icon);
    }

    return pPTB;
}

// 设置按钮风格
MyPushButton* newFuncWithIndex(QString strTitle, uint iKey, const QString strTip, const QIcon &icon)
{
    MyPushButton * pFunc = new MyPushButton(strTitle, iKey);
    pFunc->setToolTip(strTip);
    pFunc->setStatusTip(strTip);
    pFunc->setFlat(true);
    pFunc->setIcon(icon);

    return pFunc;
}

QString GetCurrentTime(const SHOWTIME_TYPE eTimeType)
{
    QString strTime;
    QDateTime CurTime = QDateTime::currentDateTime();   //获取当前时间

    switch (eTimeType) {
        case SHOWTIME_TYPE_STAMP:
        {
            // 获取当前时间戳
            strTime = QString::number(CurTime.toTime_t());
            break;
        }
        case SHOWTIME_TYPE_YYYYMMDDhhmmss:
        {
            // 获取当前时间
            strTime = CurTime.toString("yyyy-MM-dd hh:mm:ss");
            break;
        }
        case SHOWTIME_TYPE_hhmmss:
        {
            strTime = CurTime.toString("hh:mm:ss");
            break;
        }
        default:

            break;
    }

    return strTime;
}

int CountNumber(unsigned int iNum)
{
    int iCnt = 1;
    while (iNum > 0) {
        iNum = iNum / 10;
        iCnt++;
    }
    return iCnt;
}

QString GetFileFromFileDialog(QWidget *parent, const QString &strFileParam)
{
    QString curPath = QCoreApplication::applicationDirPath(); //获取应用程序的路径

    //调用打开文件对话框打开一个文件
    return QFileDialog::getOpenFileName(parent, QString("打开文件"), curPath, strFileParam);
}

// 将数据写入到文件中
bool WriteContext2File(const QString &strFileName, const QString &strContext)
{
    bool bRet = false;

    do
    {
        if(strFileName.isEmpty() || strContext.isEmpty())
        {
            qDebug() << "WriteContext2File strFileName " << strFileName << " or strContext " << strContext.size() << " is empty!";
            break;
        }

        QFile file(strFileName);
        if(!file.open(QIODevice::WriteOnly))//以写的方式打开文件，如果文件不存在则创建，
        {
            qDebug() << "ERR: " << file.errorString();
        }

        QByteArray buf = strContext.toUtf8();
        file.write(buf);//写入文件，支持QByteArray和 char * 类型数据写入
        file.close();//关闭文件

        bRet = true;

    }while (0);

    return bRet;
}

// 日志记录MessageHandler
// 参考来源：https://blog.csdn.net/qq_39736982/article/details/83027220
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    static QMutex mutex;
    mutex.lock();

    QString text;
    switch(type)
    {
    case QtDebugMsg:
        text = QString("Debug:");
        break;

    case QtWarningMsg:
        text = QString("Warning:");
        break;

    case QtCriticalMsg:
        text = QString("Critical:");
        break;

    case QtFatalMsg:
        text = QString("Fatal:");
        break;
    default:
        break;
    }

    QString context_info = QString("<%1 : %2>").arg(QString(context.file)).arg(context.line);                   // 记录源代码位置
    QString current_date = QString("<%1>").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));   // 获取时间值
    QString message = QString("%1 %2 %3 %4").arg(current_date, text, context_info, msg);

    QFile file(GLOBAL_RUNNING_LOGNAME);
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream text_stream(&file);
    text_stream << message << "\r\n";
    file.flush();
    file.close();

    mutex.unlock();
}

bool FileExist(const QString &strFileName)
{
    QFile file(strFileName);
    return file.exists();
}

// [C++] 以二进制方式从文件中读取数据
bool ReadFileByBinary(const std::string &strFile, std::string &strContext)
{
    bool bRet = false;
    std::ifstream inputStream;
    do
    {
        strContext.clear();
        if(strFile.empty())
        {
            break;
        }

        inputStream.open(strFile, std::ios::in | std::ios::binary);
        if(!inputStream.is_open())
        {
            break;
        }

        const std::string text((std::istreambuf_iterator<char>(inputStream)), (std::istreambuf_iterator<char>()));
        strContext.assign(text);

        bRet = true;
    }while(0);

    if(inputStream.is_open())
    {
        inputStream.close();
    }

    return bRet;
}

// [C++] 以二进制方式向文件中写入数据
bool WriteBinary2File(const std::string &strFile, std::string &strContext)
{
    bool bRet = false;

    std::ofstream outfile;
    do
    {
        if(strFile.empty() || strContext.empty())
        {
            break;
        }

        outfile.open(strFile, std::ios::out | std::ios::binary);
        if(!outfile.is_open())
        {
            break;
        }

        outfile << strContext << std::endl;

        bRet = true;
    }while(0);

    if(outfile.is_open())
    {
        outfile.close();
    }

    return bRet;
}

// 判断输入报文是否为有效json报文
bool isValidJSON(std::string strJson)
{
    bool bRet = false;
    cJSON *pRoot = nullptr;
    pRoot = cJSON_Parse(strJson.c_str());
    if(nullptr != pRoot)
    {
        bRet = true;
    }

    if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    return bRet;
}

// 将json报文转换为无格式化报文
bool getUnformattedcJSON(std::string &strJson)
{
    bool bRet = false;
    cJSON *pRoot = nullptr;
    char *pRet = nullptr;
    do
    {
        if(strJson.empty())
        {
            break;
        }

        pRoot = cJSON_Parse(strJson.c_str());
        if(nullptr != pRoot)
        {
            bRet = true;
        }

        pRet = cJSON_PrintUnformatted(pRoot);
        if(nullptr == pRet)
        {
            break;
        }

        strJson.clear();
        strJson.assign(pRet);

        bRet = true;
    } while (0);

    if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    if(nullptr != pRet)
    {
        free(pRet);
        pRet = nullptr;
    }

    return bRet;
}

// 日志控制台格式化打印
void appendLogMsg(QTextBrowser *pQTextBrowser, const MSG_TYPE iType, const QString &str)
{
    if(nullptr == pQTextBrowser)
    {
        return;
    }

    QString time = QDateTime::currentDateTime().toString("hh:mm:ss");
    QString strHtml;
    switch (iType) {
    case MSG_TYPE_INFO:
         strHtml = QString("<table>"
                        "<tr>"
                            "<td><img height=\"14\" src=:/operationIcons/resource/operationIcons/information_succeed.ico></td>"
                            "<td style=\"vertical-align: middle; font-size:14px\"> &nbsp; %1 &nbsp; </td>"
                            "<td style=\"vertical-align: middle; font-size:14px; color:#008000\"> %2 </td>"
                        "</tr>"
                        "</table>").arg(time, str);
        break;
    case MSG_TYPE_ERROR:
        strHtml = QString("<table>"
                       "<tr>"
                           "<td><img height=\"14\" src=:/operationIcons/resource/operationIcons/information_ban.ico></td>"
                           "<td style=\"vertical-align: middle; font-size:14px\"> &nbsp; %1 &nbsp; </td>"
                           "<td style=\"vertical-align: middle; font-size:14px; color:#DC143C\"> %2 </td>"
                       "</tr>"
                       "</table>").arg(time, str);
        break;
    case MSG_TYPE_WARN:
        strHtml = QString("<table>"
                       "<tr>"
                           "<td><img height=\"14\" src=:/operationIcons/resource/operationIcons/information_warn.ico></td>"
                           "<td style=\"vertical-align: middle; font-size:14px\"> &nbsp; %1 &nbsp; </td>"
                           "<td style=\"vertical-align: middle; font-size:14px; color:#FFD700\"> %2 </td>"
                       "</tr>"
                       "</table>").arg(time, str);
        break;
    default:
        break;
    }

    pQTextBrowser->insertHtml(strHtml);
}

// 从图片二进制数据中提取图片类型
IMG_TYPE getImgTypeFromBinary(const std::string strImg)
{
    if(strImg.empty() || strImg.size() <= 4)
    {
        return IMG_TYPE_UNKNOW;
    }

    if(strImg[1] == 'P' && strImg[2] == 'N' && strImg[3] == 'G')
    {
        return IMG_TYPE_PNG;
    }
    else if(strImg[0] == 'G' && strImg[1] == 'I' && strImg[2] == 'F')
    {
        return IMG_TYPE_GIF;
    }
    else if(strImg[6] == 'J' && strImg[7] == 'F' && strImg[8] == 'I' && strImg[9] == 'F')
    {
        return IMG_TYPE_JPG;
    }
    else
    {
        ;
    }

    return IMG_TYPE_UNKNOW;
}

// 将十进制数字转换为指定位数的十六进制数字
QString getNum2HexString(int iNum, int prec)
{
    QString strNum = QString::number(iNum, 16).toUpper();
    if(strNum.size() < prec)
    {
        QString strZero;
        strZero.fill('0', prec - strNum.size());
        strNum = strZero + strNum;
    }

    return strNum;
}

// 判断所在系统是否为大端存储
bool IsBigEndian()
{
    int a = 0x1234;
    char b =  *(char *)&a;  //通过将int强制类型转换成char单字节，通过判断起始存储位置。即等于 取b等于a的低地址部分
    if( b == 0x12)
    {
        return true;
    }
    return false;
}

// 获取本机IP地址
QString getMyIpString()
{
    QString ip_address;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // 获取所有ip
    for (int i = 0; i < ipAddressesList.size(); ++i)
    {
        QHostAddress ipAddr = ipAddressesList.at(i);
        if ((ipAddr.protocol() == QAbstractSocket::IPv4Protocol))// 筛选出ipv4
        {
            if (!ipAddr.toString().startsWith("169"))// 过滤掉网卡，路由器等的ip
            {
                ip_address = ipAddr.toString();
                break;
            }
        }
    }

    if (ip_address.isEmpty())
    {
        ip_address = QHostAddress(QHostAddress::LocalHost).toString();
    }

    return ip_address;
}

// 检查输入字符串是否为有效IPv4地址
bool checkIPv4(const QString &strSrc)
{
    return true; // 实现有待整理

    if(strSrc.isEmpty())
    {
        return false;
    }

    std::string strIP = strSrc.toStdString();
    std::regex strValidip("(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])");
    if (!regex_match(strIP, strValidip))
    {
        return false;
    }

    return true;
}

bool checkValidNumber(QString &strSrc)
{
    if(strSrc.isEmpty())
    {
       return false;
    }
    /*
    for(auto &it : strSrc)
    {
        if(!(it >= '0' && it <= '9'))
        {
            return false;
        }
    }

    return true;*/
    std::regex strValidip("^[+|-]?(([0-9]+\\.[0-9]*)|([0-9]*\\.[0-9]+)|([0-9]+))([e|E][+|-]?[0-9]+)?$");
    if (!regex_match(strSrc.toStdString(), strValidip))
    {
        return false;
    }

    return true;
}

// 中文路径支持
QString getPath2Chinese(const QString &strPath)
{
    return QTextCodec::codecForName("GB2312")->fromUnicode(strPath);
}

// JSON文本转义
bool getJSON2Translation(const std::string &strContext, std::string &strResult)
{
    strResult.clear();
    if(strContext.empty())
    {
        return false;
    }

    size_t idx = 0;
    char preCh;
    for(auto it : strContext)
    {
        if(0 != idx && '\\' != preCh && '\"' == it)
        {
            strResult += '\\';
        }

        strResult += it;
        preCh = it;
        idx++;
    }

    return true;
}

// 转义JSON文本去转义
bool getTranslation2JSON(const std::string &strContext, std::string &strResult)
{
    strResult.clear();
    if(strContext.empty())
    {
        return false;
    }

    std::map<std::string, std::string> strMap;
    strMap.clear();
    strMap["\"{"] = "}\"";
    strMap["\"["] = "]\"";

    std::string strTmp;
    strTmp.clear();
    for(auto it : strContext)
    {
        if('\\' != it)
        {
            strTmp += it;
        }
    }

    std::string strIdx;
    strIdx.clear();
    strResult.clear();
    int preIdx = -1;
    for(size_t idx = 0; idx < strTmp.length(); idx++)
    {
        if(0 == idx)
        {
            strResult += strTmp[idx];
            continue;
        }

        std::string str;
        str += strTmp[idx - 1];
        str += strTmp[idx];
        if("]\"" == str || "}\"" == str)
        {
            if(!strIdx.empty() && strMap[strIdx] == str)
            {
                // 符合规则
                std::string strRet;
                if(true == getJSON2Translation(strTmp.substr(preIdx, idx - preIdx), strRet))
                {
                    strResult += strRet;
                    strResult += '\"';
                    strIdx.clear();
                    preIdx = -1;
                }
            }
        }
        else if("\"{" == str || "\"[" == str)
        {
            if(strIdx.empty())
            {
                strIdx = str;
                preIdx = idx;
            }
        }
        else
        {
            strResult += (true == strIdx.empty() ? strTmp[idx] : ' ');
        }
    }

    return true;
}

// 根据相对坐标值在图片上画框
#if 0
bool picDrawRect(QImage &pImg, const IMG_RECT_FLOAT &stRect, const QRgb pColour, const uint pSize)
{
    // 根据相对坐标获取有效像素点位置
    uint iImgWidth = pImg.width();
    uint iImgHeight = pImg.height();
    QRect stRealRect1, stRealRect2;
    if(stRect.fX >= 0 && stRect.fX <= 1.0 &&
       stRect.fY >= 0 && stRect.fY <= 1.0 &&
       stRect.fWidth >= 0 && stRect.fWidth <= 1.0 &&
       stRect.fHeight >= 0 && stRect.fHeight <= 1.0)
    {
        stRealRect1.setX(iImgWidth * stRect.fX);
        stRealRect1.setY(iImgHeight * stRect.fY);
        stRealRect2.setX(iImgWidth * ((stRect.fX + stRect.fWidth) > 1 ? 1 : (stRect.fX + stRect.fWidth)));
        stRealRect2.setY(iImgHeight *((stRect.fY + stRect.fHeight) > 1 ? 1 : (stRect.fY + stRect.fHeight)));
    }
    else if(stRect.fX > 1.0 && stRect.fY > 1.0 && stRect.fWidth > 1.0 && stRect.fHeight > 1.0)
    {
        stRealRect1.setX(stRect.fX);
        stRealRect1.setY(stRect.fY);
        stRealRect2.setX(stRect.fX + stRect.fWidth);
        stRealRect2.setY(stRect.fY + stRect.fHeight);
    }
    else
    {
        return false;
    }

    for(uint idx = 0; idx < iImgWidth; idx++)
    {
        for(uint idy = 0; idy < iImgHeight; idy++)
        {
            if((idx >= (stRealRect1.x() - pSize) && idx <= (stRealRect1.x() + pSize)) ||
               (idx >= (stRealRect2.x() - pSize) && idx <= (stRealRect2.x() + pSize)))
            {
                if(idy >= (stRealRect1.y() - pSize) && idy <= (stRealRect2.y() + pSize))
                {
                    pImg.setPixelColor(idx, idy, pColour);
                }
            }
            else if((idy >= (stRealRect1.y() - pSize) && idy <= (stRealRect1.y() + pSize)) ||
                    (idy >= (stRealRect2.y() - pSize) && idy <= (stRealRect2.y() + pSize)))
            {
                if(idx >= (stRealRect1.x() - pSize) && idx <= (stRealRect2.x() + pSize))
                {
                    pImg.setPixelColor(idx, idy, pColour);
                }
            }
        }
    }

    return true;
}
#endif

// 根据限定范围进行校验
void checkNumWithRange(uint &iNum, const uint iMin, const uint iMax)
{
    if(iNum < iMin)
    {
        iNum = iMin;
    }
    else if(iNum > iMax)
    {
        iNum = iMax;
    }
}

// 根据归一化坐标值在图片上画框
bool picDrawRectNormalization(QImage &pImg, const IMG_RECT_FLOAT &stRect, const QRgb pColour, const uint pSize)
{
    if(pImg.isNull())
    {
        return false;
    }

    uint iImgWidth = pImg.width();
    uint iImgHeight = pImg.height();
    IMG_RECT_INT stRectInt;
    stRectInt.iX = iImgWidth * stRect.fX;
    stRectInt.iY = iImgHeight * stRect.fY;
    stRectInt.iWidth = iImgWidth * stRect.fWidth;
    stRectInt.iHeight = iImgHeight * stRect.fHeight;

    return picDrawRectLocation(pImg, stRectInt, pColour, pSize);
}

// 根据真实坐标值在图片上画框
bool picDrawRectLocation(QImage &pImg, const IMG_RECT_INT &stRect, const QRgb pColour, const uint pSize)
{
    if(pImg.isNull())
    {
        return false;
    }

    uint iImgWidth = pImg.width();
    uint iImgHeight = pImg.height();
    uint iLeftUpX = stRect.iX;
    uint iLeftUpY = stRect.iY;
    uint iRightDownX = stRect.iX + stRect.iWidth;
    uint iRightDownY = stRect.iY + stRect.iHeight;
    checkNumWithRange(iLeftUpX, 0, iImgWidth);
    checkNumWithRange(iLeftUpY, 0, iImgHeight);
    checkNumWithRange(iRightDownX, 0, iImgWidth);
    checkNumWithRange(iRightDownY, 0, iImgHeight);

    for(uint idx = 0; idx < iImgWidth; idx++)
    {
        for(uint idy = 0; idy < iImgHeight; idy++)
        {
            if((idx >= (iLeftUpX - pSize) && idx <= (iLeftUpX + pSize)) ||
               (idx >= (iRightDownX - pSize) && idx <= (iRightDownX + pSize)))
            {
                if(idy >= (iLeftUpY - pSize) && idy <= (iRightDownY + pSize))
                {
                    pImg.setPixelColor(idx, idy, pColour);
                }
            }
            else if((idy >= (iLeftUpY - pSize) && idy <= (iLeftUpY + pSize)) ||
                    (idy >= (iRightDownY - pSize) && idy <= (iRightDownY + pSize)))
            {
                if(idx >= (iLeftUpX - pSize) && idx <= (iRightDownX + pSize))
                {
                    pImg.setPixelColor(idx, idy, pColour);
                }
            }
        }
    }

    return true;
}

// 根据相对坐标在图片上画点
bool picDrawPoint(QImage &pImg, const IMG_RECT_FLOAT &stRect, const QRgb pColour, const uint pSize)
{
    uint iImgWidth = pImg.width();
    uint iImgHeight = pImg.height();
    QRect stRealRect;
    if(stRect.fX >= 0 && stRect.fX <= 1.0 && stRect.fY >= 0 && stRect.fY <= 1.0)
    {
        stRealRect.setX(iImgWidth * stRect.fX);
        stRealRect.setY(iImgHeight * stRect.fY);
    }
    else if(stRect.fX >= 1.0 && stRect.fY >= 1.0)
    {
        stRealRect.setX(stRect.fX);
        stRealRect.setY(stRect.fY);
    }
    else
    {
        return false;
    }

    for(uint idx = 0; idx < iImgWidth; idx++)
    {
        for(uint idy = 0; idy < iImgHeight; idy++)
        {
            if((idx >= (stRealRect.x() - pSize) && idx <= (stRealRect.x() + pSize)) ||
               (idx >= (stRealRect.y() - pSize) && idx <= (stRealRect.y() + pSize)))
            {
                if(idy >= (stRealRect.y() - pSize) && idy <= (stRealRect.y() + pSize))
                {
                    pImg.setPixelColor(idx, idy, pColour);
                }
            }
        }
    }

    return true;
}

// 根据路径获取图片数据
bool getImgFromPath(QImage &pImg, const QString& strPath)
{
    bool bStatus = true;
    QImageReader pReader(strPath);
    pReader.setDecideFormatFromContent(true);
    bStatus = pReader.canRead();
    if(true == bStatus)
    {
        bStatus = pReader.read(&pImg);
    }

    return bStatus;
}

// 根据路径提取文件格式，以.为依据
QString getFormatFromFile(const QString& strPath)
{
    QString strFormat;

    std::string str = strPath.toStdString();
    std::string::size_type itPos = str.find_last_of('.');
    if(std::string::npos != itPos)
    {
        std::string strSub = str.substr(itPos + 1, str.length() - itPos - 1);
        strFormat = QString::fromStdString(strSub);
    }

    return strFormat;
}